package com.ww.springboot.boot.algorithm.stack;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.TimeUnit;

/**
 * 描述：
 *
 * @author wanwei
 * @since 2021-07-24 16:21
 */
public class SudokuDemoWithSwingDemo extends JFrame {

    private static int[][] array =
            {{0, 0, 9, 7, 4, 8, 0, 0, 0},
                    {7, 0, 0, 0, 0, 0, 0, 0, 0},
                    {0, 2, 0, 1, 0, 9, 0, 0, 0},
                    {0, 0, 7, 0, 0, 0, 2, 4, 0},
                    {0, 6, 4, 0, 1, 0, 5, 9, 0},
                    {0, 9, 8, 0, 0, 0, 3, 0, 0},
                    {0, 0, 0, 8, 0, 3, 0, 2, 0},
                    {0, 0, 0, 0, 0, 0, 0, 0, 6},
                    {0, 0, 0, 2, 7, 5, 9, 0, 0}};

    private static int[][] arrayOld =
            {{0, 0, 9, 7, 4, 8, 0, 0, 0},
                    {7, 0, 0, 0, 0, 0, 0, 0, 0},
                    {0, 2, 0, 1, 0, 9, 0, 0, 0},
                    {0, 0, 7, 0, 0, 0, 2, 4, 0},
                    {0, 6, 4, 0, 1, 0, 5, 9, 0},
                    {0, 9, 8, 0, 0, 0, 3, 0, 0},
                    {0, 0, 0, 8, 0, 3, 0, 2, 0},
                    {0, 0, 0, 0, 0, 0, 0, 0, 6},
                    {0, 0, 0, 2, 7, 5, 9, 0, 0}};

    private static Stack<int[]> stack = new Stack<>();

    private static final int n = 9;


    private static int x = 0;
    private static int y = 0;

    private static Long time = 1L;

    private JButton jbt1 = new JButton("1");

    private JButton jbt2 = new JButton("2");

    private JButton jbt0 = new JButton("0");

    public SudokuDemoWithSwingDemo() {
        add(new Composition());

        JPanel jpanel1 = new JPanel();
        jpanel1.setLayout(new GridLayout(3, 2));
        jpanel1.add(jbt1);
        jpanel1.add(jbt2);
        jpanel1.add(jbt0);

        jbt1.addActionListener(e -> time = 1L);

        jbt2.addActionListener(e -> time = 2L);

        jbt0.addActionListener(e -> time = 0L);

        add(jpanel1, BorderLayout.SOUTH);
    }


    public static void main(String[] args) {
        JFrame frame = new SudokuDemoWithSwingDemo();
        frame.setTitle("数独");
        frame.setSize(830, 600);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        new Thread(() -> {
            try {
                method();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    private static void method() throws InterruptedException {
        int[] ii = new int[2];
        ii[0] = x;
        ii[1] = y;
        stack.push(ii);
        //从左上角起
        while (x < n && y < n && !stack.empty()) {
            if (array[x][y] != 0) {
                if (x < n - 1) {
                    x++;
                } else {
                    y++;
                    x = 0;
                }
            } else {
                ii = new int[2];
                ii[0] = x;
                ii[1] = y;
                while (!stack.empty()) {
                    //入栈
                    TimeUnit.SECONDS.sleep(time);
                    //如果上一个值已经是9 则直接继续退
                    if (array[x][y] == 9) {
                        TimeUnit.SECONDS.sleep(time);
                        //还原值
                        array[x][y] = 0;
                        //回退 从栈中取出上一个坐标
                        ii = stack.pop();
                        x = ii[0];
                        y = ii[1];
                        continue;
                    }
                    array[x][y]++;
                    //判断是否合法
                    while (!judge(x, y, array) && !(array[x][y] >= n)) {
                        TimeUnit.SECONDS.sleep(time);
                        array[x][y]++;
                    }
                    if (!judge(x, y, array)) {
                        TimeUnit.SECONDS.sleep(time);
                        //还原值
                        array[x][y] = 0;
                        //回退 从栈中取出上一个坐标
                        ii = stack.pop();
                        x = ii[0];
                        y = ii[1];
                    } else {
                        stack.push(ii);
                        if (x == n - 1 && y == n - 1) {
                            break;
                        }
                        if (x < n - 1) {
                            x++;
                        } else {
                            y++;
                            x = 0;
                        }
                        break;
                    }
                }
            }
        }
        x = -1;
        y = -1;
    }

    static class Composition extends JPanel {

        public Composition() {
            Timer timer = new Timer(100, new Composition.Refresh());
            timer.start();
        }


        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Color defaultColor = g.getColor();
            Font defaultFont = g.getFont();
            //画数独图
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    g.drawString(String.valueOf(array[i][j]), 110 + 30 * i, 120 + 30 * j);
                    g.drawRect(100 + 30 * i, 100 + 30 * j, 30, 30);
                }
            }

            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    if (x >= 0 && y >= 0) {
                        if (x == i && y == j) {
                            g.setColor(Color.red);
                        } else {
                            if (array[i][j] == array[x][y]
                                    && array[i][j] != 0
                                    && judgeRange(x, y, i, j)) {
                                g.setFont(new Font(null, Font.BOLD, 15));
                                g.setColor(Color.blue);
                            }
                        }
                    }
                    g.drawString(String.valueOf(array[i][j]), 110 + 30 * i, 120 + 30 * j);
                    g.setColor(defaultColor);
                    g.setFont(defaultFont);
                }
            }

            //旧图
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    g.drawString(String.valueOf(arrayOld[i][j]), 410 + 30 * i, 120 + 30 * j);
                    g.drawRect(400 + 30 * i, 100 + 30 * j, 30, 30);
                }
            }

            g.setFont(new Font(null, Font.BOLD, 20));
            g.drawString("新图", 200, 400);
            g.drawString("旧图", 500, 400);

            if (x == -1 && y == -1 && stack.empty()) {
                g.drawString("失败--无解！！！", 350, 450);
            } else {
                g.drawString("成功！！！", 350, 450);
            }
        }

        class Refresh implements ActionListener {
            @Override
            public void actionPerformed(ActionEvent e) {
                //重画
                repaint();
            }
        }

    }

    private static Boolean judge(int x, int y, int[][] arraysNew) {
        //横线不能重复
        Set<Integer> setX = new HashSet<>();
        Set<Integer> setY = new HashSet<>();
        for (int i = 0; i < n; i++) {
            if (arraysNew[x][i] == 0) {
                continue;
            }
            if (!setX.add(arraysNew[x][i])) {
                return false;
            }
        }
        //竖线不能重复
        for (int i = 0; i < n; i++) {
            if (arraysNew[i][y] == 0) {
                continue;
            }
            if (!setY.add(arraysNew[i][y])) {
                return false;
            }
        }

        Set<Integer> setNine = new HashSet<>();
        //9宫格不能重复
        int xx = x / 3;
        int yy = y / 3;

        for (int i = xx * 3; i < (xx + 1) * 3; i++) {
            for (int j = yy * 3; j < (yy + 1) * 3; j++) {
                if (arraysNew[i][j] == 0) {
                    continue;
                }
                if (!setNine.add(arraysNew[i][j])) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 判断两坐标是否在一个范围
     * X Y NINE
     *
     * @param x
     * @param y
     * @param i
     * @param j
     * @return
     */
    private static Boolean judgeRange(int x, int y, int i, int j) {

        if (x == i) {
            return true;
        }
        if (y == j) {
            return true;
        }
        if (x / 3 == i / 3 && y / 3 == j / 3) {
            return true;
        }
        return false;
    }
}
